
#include <BXmlNode>
#include "member_BXmlDocument.h"
#include "member_BXmlNode.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BXmlNode*)_ptr)
#define member_allocate()		_ptr = new member_BXmlNode(this)
#define member_release()		delete (member_BXmlNode*)_ptr

BXmlNode::BXmlNode(BXmlNode* parent)
{
	member_allocate();
	setParent(parent);
}
BXmlNode::BXmlNode(const BString& name, BXmlNode* parent) : BObject(name)
{
	member_allocate();
	setParent(parent);
}
BXmlNode::~BXmlNode()
{
	for (int i = 0; i < member.children.size(); i++)
	{
		BXmlNode* child = member.children[i];
		xml_member(child)->parent = 0;
	}
	member_release();
}

void BXmlNode::copy(const BXmlNode* other)
{
	this->setName(other->name());
	member.attribMap = xml_member(other)->attribMap;
	member.format = xml_member(other)->format;
	member.text = xml_member(other)->text;
	for (int i = 0; i < xml_member(other)->children.size(); i++)
	{
		BXmlNode* node = xml_member(other)->children[i];
		BXmlNode* child = node->clone();
		xml_member(this)->children.append(child);
	}
}
BXmlNode* BXmlNode::clone() const
{
	BXmlNode* node = new BXmlNode();
	node->copy(this);
	return node;
}

bool BXmlNode::empty() const
{
	return member.text.empty() && member.attribMap.empty() && member.children.empty();
}
bool BXmlNode::dirty() const
{
	return member.textDirty;
}

bool BXmlNode::setFormat(const BString& format)
{
	if (member.format.text != format)
	{
		member.format.fromString(format);
		member.raiseSignal(Signal_Changed);
		return true;
	}
	return true;
}
const BString& BXmlNode::format() const
{
	return member.format.text;
}

bool BXmlNode::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
	}
	return true;
}
const BString& BXmlNode::text() const
{
	return member.text;
}

void BXmlNode::appendText(const BString& text)
{
	if (member.text.append(text))
	{
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
	}
}
void BXmlNode::prependText(const BString& text)
{
	if (member.text.prepend(text))
	{
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
	}
}

const BString& BXmlNode::plainText() const
{
	if (member.textDirty)
	{
		member.plainText.reset();
		member.plainText.append(member.text);
		for (int i = 0; i < member.children.size(); i++)
		{
			const BString& text = member.children[i]->plainText();
			member.plainText.append(text);
		}
		member.textDirty = false;
	}
	return member.plainText;
}

void BXmlNode::setAttribMap(const BMap<BString, BString>& attribMap)
{
	member.attribMap = attribMap;
	member.raiseSignal(Signal_Changed);
}
const BMap<BString, BString>& BXmlNode::attribMap() const
{
	return member.attribMap;
}

bool BXmlNode::setAttrib(const BString& name, const BString& value)
{
	if (name.empty())
		return false;
	member.attribMap[name] = value;
	member.raiseSignal(Signal_Changed);
	return true;
}
const BString& BXmlNode::attrib(const BString& name) const
{
	return member.attribMap(name);
}

bool BXmlNode::containAttrib(const BString& name) const
{
	return member.attribMap.contain(name);
}
bool BXmlNode::removeAttrib(const BString& name)
{
	if (member.attribMap.remove(name))
	{
		member.raiseSignal(Signal_Changed);
		return true;
	}
	return false;
}
bool BXmlNode::clearAttribs()
{
	if (member.attribMap.empty())
		return false;
	member.attribMap.clear();
	member.raiseSignal(Signal_Changed);
	return true;
}

const BXmlDocument* BXmlNode::document() const
{
	return member.document;
}
BXmlDocument* BXmlNode::document()
{
	return member.document;
}

const BXmlNode* BXmlNode::root() const
{
	const BXmlNode* node = this;
	while (xml_member(node)->parent)
		node = xml_member(node)->parent;
	return node;
}
BXmlNode* BXmlNode::root()
{
	BXmlNode* node = this;
	while (xml_member(node)->parent)
		node = xml_member(node)->parent;
	return node;
}

void BXmlNode::setParent(BXmlNode* parent)
{
	if (parent && parent != this)
	{
		parent->addChild(this);
	}
}
const BXmlNode* BXmlNode::parent() const
{
	return member.parent;
}
BXmlNode* BXmlNode::parent()
{
	return member.parent;
}

BXmlNode* BXmlNode::parent(const BString& name)
{
	if (name.empty())
		return 0;
	BXmlNode* parent = member.parent;
	while (parent && parent->name() != name)
		parent = xml_member(parent)->parent;
	return parent;
}
const BXmlNode* BXmlNode::parent(const BString& name) const
{
	if (name.empty())
		return 0;
	const BXmlNode* parent = member.parent;
	while (parent && parent->name() != name)
		parent = xml_member(parent)->parent;
	return parent;
}

BXmlNode* BXmlNode::addText(const BString& text)
{
	BXmlNode* textNode = new BXmlNode();
	textNode->setText(text);
	if (member.children.append(textNode))
	{
		xml_member(textNode)->document = member.document;
		xml_member(textNode)->parent = this;
		member.textDirty = true;
		return textNode;
	}
	return 0;
}
BXmlNode* BXmlNode::addChild(const BString& name, const BString& text)
{
	BXmlNode* child = new BXmlNode(name);
	child->setText(text);
	if (member.children.append(child))
	{
		xml_member(child)->document = member.document;
		xml_member(child)->parent = this;
		member.textDirty = true;
		return child;
	}
	return 0;
}
bool BXmlNode::addChild(BXmlNode* child)
{
	if (!child)
		return false;
	if (child == this)
		return false;
	if (child == member.parent)
		return false;
	if (!member.children.contain(child))
	{
		member.children.append(child);
		if (xml_member(child)->parent)
			xml_member(child)->parent->removeChild(child);
		xml_member(child)->parent = this;
		member.textDirty = true;
		object_member(child)->emit(Signal_ParentChanged, BValue(this, true));
		member.raiseSignal(Signal_Changed);
		return true;
	}
	return false;
}

BXmlNode* BXmlNode::insertText(int pos, const BString& text)
{
	BXmlNode* textNode = new BXmlNode();
	textNode->setText(text);
	if (member.children.insert(pos, textNode))
	{
		xml_member(textNode)->document = member.document;
		xml_member(textNode)->parent = this;
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
		return textNode;
	}
	return 0;
}
BXmlNode* BXmlNode::insertChild(int pos, const BString& name, const BString& text)
{
	if (pos < 0 || pos > member.children.size())
		return 0;
	BXmlNode* child = new BXmlNode(name);
	child->setText(text);
	if (member.children.insert(pos, child))
	{
		xml_member(child)->document = member.document;
		xml_member(child)->parent = this;
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
		return child;
	}
	return 0;
}
bool BXmlNode::insertChild(int pos, BXmlNode* node)
{
	if (!node)
		return false;
	if (node == this)
		return false;
	if (node == member.parent)
		return false;
	if (xml_member(node)->parent == this)
	{
		int index = member.children.find(node);
		member.children.move(index, pos);
		member.textDirty = true;
		this->emit(Signal_OrderChanged);
		return true;
	}
	if (member.children.insert(pos, node))
	{
		if (xml_member(node)->parent)
			xml_member(node)->parent->removeChild(node);
		xml_member(node)->parent = this;
		member.textDirty = true;
		member.raiseSignal(Signal_Insert, BValue(node, this));
		return true;
	}
	return false;
}

bool BXmlNode::replaceChild(BXmlNode* child, BXmlNode* node)
{
	if (!child || child == node)
		return false;
	if (xml_member(child)->parent != this)
		return false;

	BHolder<BXmlNode> holder(child);
	if (node)
	{
		int index = member.children.find(child);
		member.children[index] = node;
		if (xml_member(node)->parent)
			xml_member(node)->parent->removeChild(node);
		xml_member(node)->parent = this;
	}
	else
	{
		member.children.remove(node);
	}
	member.textDirty = true;
	member.raiseSignal(Signal_Changed);
	return true;
}

bool BXmlNode::removeChild(int pos)
{
	if (member.children.check(pos))
	{
		BXmlNode* child = member.children[pos];
		xml_member(child)->document = 0;
		xml_member(child)->parent = 0;
		BHolder<BXmlNode> holder(child);
		member.children.remove(pos);
		member.textDirty = true;
		member.raiseSignal(Signal_Remove, BValue(child, this));
		return true;
	}
	return false;
}
bool BXmlNode::removeChild(const BString& name)
{
	for (int i = 0; i < member.children.size(); i++)
	{
		BXmlNode* child = member.children[i];
		if (child->name() == name)
		{
			return removeChild(child);
		}
	}
	return false;
}
bool BXmlNode::removeChild(const BXmlNode* node)
{
	if (!node)
		return false;
	if (node == this)
		return false;
	if (node == member.parent)
		return false;
	if (xml_member(node)->parent == this)
	{
		xml_member(node)->document = 0;
		xml_member(node)->parent = 0;
		member.children.remove(node);
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
		return true;
	}
	return false;
}

bool BXmlNode::clearChildren()
{
	if (int count = member.children.size())
	{
		for (int i = 0; i < count; i++)
		{
			BXmlNode* child = member.children[i];
			xml_member(child)->parent = 0;
		}
		member.children.clear();
		member.plainText.clear();
		member.textDirty = true;
		member.raiseSignal(Signal_Changed);
		return true;
	}
	return false;
}

int BXmlNode::childCount() const
{
	return member.children.size();
}
BXmlNode* BXmlNode::child(int index)
{
	return member.children(index);
}
const BXmlNode* BXmlNode::child(int index) const
{
	return member.children(index);
}

BXmlNode* BXmlNode::child(const BString& desc)
{
	if (XmlFilter::Check(desc))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
				return child;
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
				return child;
		}
	}
	return 0;
}
const BXmlNode* BXmlNode::child(const BString& desc) const
{
	if (XmlFilter::Check(desc))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
				return child;
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
				return child;
		}
	}
	return 0;
}

BXmlNode* BXmlNode::find(const BString& desc)
{
	if (XmlFilter::Check(desc))
	{
		XmlFilter filter(desc);
		BXmlNode* node = member.find(filter);
		return node;
	}
	if (this->name() == desc)
		return this;
	for (int i = 0; i < member.children.size(); i++)
	{
		BXmlNode* child = member.children[i];
		BXmlNode* node = child->find(desc);
		if (node)
			return node;
	}
	return 0;
}
const BXmlNode* BXmlNode::find(const BString& desc) const
{
	if (XmlFilter::Check(desc))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			BXmlNode* node = child->find(desc);
			if (node)
				return node;
		}
	}
	else
	{
		if (this->name() == desc)
			return this;
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			BXmlNode* node = child->find(desc);
			if (node)
				return node;
		}
	}
	return 0;
}

int BXmlNode::level() const
{
	int level = 0;
	const BXmlNode* node = this->parent();
	while (node)
	{
		level++;
		node = xml_member(node)->parent;
	}
	return level;
}

bool BXmlNode::setIndex(int index)
{
	if (member.parent)
	{
		BArray<BXmlNodeHolder>& children = xml_member(member.parent)->children;
		int pos = children.find(this);
		if (children.move(pos, index))
		{
			member.raiseSignal(Signal_OrderChanged, index);
			return true;
		}
	}
	else
	{
		BArray<BXmlNodeHolder>& nodes = xmldoc_member(member.document)->nodes;
		int pos = nodes.find(this);
		if (nodes.move(pos, index))
		{
			member.raiseSignal(Signal_OrderChanged, index);
			return true;
		}
	}
	return false;
}
int BXmlNode::index() const
{
	if (BXmlNode* parent = member.parent)
	{
		BArray<BXmlNodeHolder>& children = xml_member(parent)->children;
		return children.find(this);
	}
	if (BXmlDocument* document = member.document)
	{
		BArray<BXmlNodeHolder>& nodes = xmldoc_member(document)->nodes;
		return nodes.find(this);
	}
	return -1;
}

bool BXmlNode::raise()
{
	if (member.parent)
	{
		BXmlNodeHolderArray& children = xml_member(member.parent)->children;
		int pos = children.find(this);
		if (children.raise(this))
		{
			member.raiseSignal(Signal_OrderChanged);
			return true;
		}
	}
	if (member.document)
	{
		BXmlNodeHolderArray& nodes = xmldoc_member(member.document)->nodes;
		int pos = nodes.find(this);
		if (nodes.raise(this))
		{
			member.raiseSignal(Signal_OrderChanged);
			return true;
		}
	}
	return false;
}
bool BXmlNode::lower()
{
	if (member.parent)
	{
		BXmlNodeHolderArray& children = xml_member(member.parent)->children;
		int pos = children.find(this);
		if (children.move(pos, 0))
		{
			member.raiseSignal(Signal_OrderChanged);
			return true;
		}
	}
	else
	{
		BXmlNodeHolderArray& nodes = xmldoc_member(member.document)->nodes;
		int pos = nodes.find(this);
		if (nodes.move(pos, 0))
		{
			member.raiseSignal(Signal_OrderChanged);
			return true;
		}
	}
	return false;
}

BXmlNode* BXmlNode::prev()
{
	if (member.parent)
	{
		BArray<BXmlNodeHolder>& up_children = xml_member(member.parent)->children;
		int index = up_children.find(this);
		if (index > 1)
			return up_children[index - 1];
	}
	return 0;
}
BXmlNode* BXmlNode::next()
{
	if (member.parent)
	{
		BArray<BXmlNodeHolder>& up_children = xml_member(member.parent)->children;
		int index = up_children.find(this);
		if (index < up_children.size() - 1)
			return up_children[index + 1];
	}
	return 0;
}

bool BXmlNode::above(const BXmlNode* node) const
{
	if (!node)
		return false;
	BXmlNode* parent = xml_member(node)->parent;
	while (parent)
	{
		if (parent == this)
			return true;
		parent = xml_member(parent)->parent;
	}
	return false;
}
bool BXmlNode::under(const BXmlNode* node) const
{
	BXmlNode* parent = member.parent;
	while (parent)
	{
		if (parent == node)
			return true;
		parent = xml_member(parent)->parent;
	}
	return false;
}
bool BXmlNode::contain(const BXmlNode* node) const
{
	if (!node)
		return false;
	for (int i = 0; i < member.children.size(); i++)
	{
		if (member.children[i] == node)
			return true;
	}
	return false;
}

BXmlNode* BXmlNode::create(const BString& path)
{
	int end = path.find('/');
	if (end == path.size())
	{
		BXmlNode* node = this->child(path);
		if (node == 0)
			node = this->addChild(path);
		return node;
	}
	BString text;
	path.substr(text, 0, end);
	BXmlNode* node = this->child(text);
	if (node == 0)
	{
		node = this->addChild(text);
	}
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		BXmlNode* child = node->child(text);
		if (child == 0)
			child = node->addChild(text);
		node = child;
		pos = end + 1;
	}
	return node;
}

BXmlNode* BXmlNode::locate(const BString& desc)
{
	BStringArray namelst = desc.split('/');
	BXmlNode* node = this->child(namelst.first());
	namelst.remove(0);
	while (node && namelst.size())
	{
		const BString& text = namelst.first();
		node = node->child(text);
		namelst.remove(0);
	}
	return node;
}
const BXmlNode* BXmlNode::locate(const BString& desc) const
{
	BStringArray namelst = desc.split('/');
	const BXmlNode* node = this->child(namelst.first());
	namelst.remove(0);
	while (node && namelst.size())
	{
		const BString& text = namelst.first();
		node = node->child(text);
		namelst.remove(0);
	}
	return node;
}

bool BXmlNode::collect(const BString& desc, BArray<BXmlNode*>& result)
{
	result.reset();
	if (XmlFilter::Check(desc))
	{
		XmlFilter filter(desc);
		member.collect(filter, result);
	}
	else
	{
		if (this->name() == desc)
			return this;
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			xml_member(child)->collect(desc, result);
		}
	}
	return result.size();
}
bool BXmlNode::collect(const BString& desc, BArray<const BXmlNode*>& result) const
{
	result.reset();
	if (XmlFilter::Check(desc))
	{
		XmlFilter filter(desc);
		member.collect(filter, (BArray<BXmlNode*>&)result);
	}
	else
	{
		if (this->name() == desc)
			result.append(this);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			xml_member(child)->collect(desc, (BArray<BXmlNode*>&)result);
		}
	}
	return result.size();
}

BArray<BXmlNode*> BXmlNode::collect(const BString& desc)
{
	BArray<BXmlNode*> result;
	this->collect(desc, result);
	return result;
}
BArray<const BXmlNode*> BXmlNode::collect(const BString& desc) const
{
	BArray<const BXmlNode*> result;
	this->collect(desc, result);
	return result;
}

BString BXmlNode::path(const char* split) const
{
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}
BString BXmlNode::path(const BString& split) const
{
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}

BString BXmlNode::pathTo(const BXmlNode* parent, const char* split) const
{
	if (this == parent)
		return BString();
	if (parent && !this->under(parent))
		return BString();
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node && node != parent)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}
BString BXmlNode::pathTo(const BXmlNode* parent, const BString& split) const
{
	if (this == parent)
		return BString();
	if (parent && !this->under(parent))
		return BString();
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node && node != parent)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}
